/* xdaliclock - a melting digital clock
 * Copyright (c) 1991-2006 Jamie Zawinski <jwz@jwz.org>
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.  No representations are made about the suitability of this
 * software for any purpose.  It is provided "as is" without express or
 * implied warranty.
 */

//#import <CoreSurface/CoreSurface.h>
//#import <GraphicsServices/GraphicsServices.h>
#import "DaliClockView.h"
#import "xdaliclock.h"
#import "hsv.h"
#import <sys/time.h>

static DaliClockView *sharedInstance = nil;
void updateScreen() {
	[sharedInstance performSelectorOnMainThread:@selector(updateScreen) withObject:nil waitUntilDone: NO];
}

int __screenOrientation;

@implementation DaliClockView 
- (id)initWithFrame:(struct CGRect)frame {
	NSLog(@"fsw: %f, fsh: %f", frame.size.width, frame.size.height);
	
	if ((self == [super initWithFrame:frame])!=nil) {
            sharedInstance = self;
            initGraphics = 0;
	}
	
	self = [super initWithFrame:frame];
  
	memset (&config, 0, sizeof(config));

	config.max_fps = 12;
	config.max_cps = 12;
	config.twelve_hour_p = 1;
	
	fgh = 1.0 * (240.0/360.0);
	bgh = 0.5;
	fgs = bgs = 1.0;
	fgv = bgv = 0.7;
	
	fgcolor = 0x0000ff00;
	bgcolor = 0x0000;
	
	
	

	// initialize the fonts and bitmaps
	[self intializeDigits:frame];

	[self clockTick];
	[self colorTick];

	return self;
}

- (void)updateScreen {
	[sharedInstance setNeedsDisplay];
}

- (void)dealloc {
        //[ screenLayer release ];
        pthread_mutex_destroy(&screenUpdateMutex);
        pthread_cond_destroy(&screenUpdateLock);
	[super dealloc];
}

- (void)drawRect:(CGRect)rect{
	NSRect framerect, fromrect, torect;
  
  framerect.size.width = [self frame].size.width;
  framerect.size.height = [self frame].size.height;
  
  framerect.origin.x = framerect.origin.y = 0;
  fromrect.origin.x = fromrect.origin.y = 0;
  fromrect.size.width = config.width;
  fromrect.size.height = config.height;

  if (config.width <= 0 || config.height <= 0) abort();

  float img_aspect = (float) config.width / (float) config.height;
  float win_aspect = framerect.size.width / (float) framerect.size.height;

  // Scale the image to fill the window without changing its aspect ratio.
  //
  if (win_aspect > img_aspect) {
    torect.size.height = framerect.size.height;
    torect.size.width  = framerect.size.height * img_aspect;
  } else {
    torect.size.width  = framerect.size.width;
    torect.size.height = framerect.size.width / img_aspect;
  }

  // The animation slows down a lot if we use truly gigantic numbers,
  // so limit the number size in screensaver-mode.
  //
  /*if (constrainSizes) {
    int maxh = (config.time_mode == SS ? 512 : /*256 200);
    if (torect.size.height > maxh) {
      torect.size.height = maxh;
      torect.size.width  = maxh * img_aspect;
    }
  }*/

  // put a margin between the numbers and the edge of the window.
  torect.size.width  *= 0.95;  // 5% horizontally
  torect.size.height *= 0.80;  // 20% vertically

//  torect.size = fromrect.size; // #### debugging: don't scale

  // center it in the window
  //
  torect.origin.x = (framerect.size.width  - torect.size.width ) / 2;
  torect.origin.y = (framerect.size.height - torect.size.height) / 2;

  /* This uses lower level Quartz/CoreGraphics calls to do the drawing.
     It might be more efficient, but it's way more complicated...
   */

  float fgr, fgg, fgb, fga;
  float bgr, bgg, bgb, bga;
  
  CGColorRef fgColor = [UIColor colorWithHue:fgh saturation:fgs brightness:fgv alpha:1.0].CGColor;
  CGColorRef bgColor = [UIColor colorWithHue:bgh saturation:bgs brightness:bgv alpha:1.0].CGColor;
 
  float *fgComponents = CGColorGetComponents(fgColor);
    
  fgr = fgComponents[0];
  fgg = fgComponents[1];
  fgb = fgComponents[2];
  fga = fgComponents[3];

float *bgComponents = CGColorGetComponents(bgColor);
    
  bgr = bgComponents[0];
  bgg = bgComponents[1];
  bgb = bgComponents[2];
  bga = bgComponents[3];

	
  CGDataProviderRef provider =
    CGDataProviderCreateWithData (NULL /* info */, config.bitmap,
                                  config.height * (config.width>>3),
                                  NULL);
  float decode[] = { 1.0, 0.0 };  /* invert pixels */
  CGImageRef fgmask=0, bgmask=0;
  fgmask = CGImageMaskCreate (config.width, config.height,
                              1 /* bitsPerComponent */,
                              1 /* bitsPerPixel */,
                              config.width>>3 /* bytesPerRow */,
                              provider, decode,
                              NO /* shouldInterpolate */
                              );
  if (fga < 1.0)
    bgmask = CGImageMaskCreate (config.width, config.height,
                                1 /* bitsPerComponent */,
                                1 /* bitsPerPixel */,
                                config.width>>3 /* bytesPerRow */,
                                provider, NULL /* decode */,
                                NO /* shouldInterpolate */
                                );
  CGDataProviderRelease (provider);

CGContextRef cgc = UIGraphicsGetCurrentContext();
  CGRect bgrect, fgrect;
  bgrect.origin.x    = framerect.origin.x;
  bgrect.origin.y    = framerect.origin.y;
  bgrect.size.width  = framerect.size.width;
  bgrect.size.height = framerect.size.height;
  fgrect.origin.x    = torect.origin.x;
  fgrect.origin.y    = torect.origin.y;
  fgrect.size.width  = torect.size.width;
  fgrect.size.height = torect.size.height;

  if (! bgmask) {
    /* foreground is solid; background is solid or transparent.
       we can just blast the background down as a rectangle.
     */
    CGContextSetRGBFillColor (cgc, bgr,bgg,bgb,bga);
    if (bga < 1.0)
      CGContextClearRect (cgc, bgrect);
    CGContextFillRect (cgc, bgrect);

  } else {
    /* foreground is transparent; background is solid or transparent.
       we have to draw the background with a mask so that it doesn't
       interfere with the foreground.
     */
    CGContextSaveGState (cgc);
    CGContextSetRGBFillColor (cgc, bgr,bgg,bgb,bga);
    CGContextClearRect (cgc, bgrect);

    /* Ok, this is fucked up.  Since our "mask" bitmap is not as large as
       the window itself, and there's no way to specify a mask origin for
       the inverse mask (that is, say "draw everything *but* this mask)
       we have to fill in the rectangles outside the masked rectangle first.
       But, this leaves artifacts!  Even though the rectangles line up
       exactly, there's a transparent box getting left around the characters.
       Or, if the retangles overlap, then we get a too-dark box.  SHIT!
     */
    CGRect rects[4];
    rects[0] = bgrect;
    rects[0].size.height = fgrect.origin.y - bgrect.origin.y /*+ 1*/;
    rects[1] = rects[0];
    rects[1].origin.y = fgrect.origin.y + fgrect.size.height /*- 1*/;
    rects[2] = rects[1];
    rects[2].origin.y = fgrect.origin.y;
    rects[2].size.width = fgrect.origin.x - bgrect.origin.x /*+ 1*/;
    rects[2].size.height = fgrect.size.height;
    rects[3] = rects[2];
    rects[3].origin.x = bgrect.origin.x+fgrect.origin.x+fgrect.size.width/*-1*/;
    rects[3].size.width = bgrect.size.width-fgrect.size.width-fgrect.origin.x;
    rects[3].size.height = fgrect.size.height;

    CGContextFillRects (cgc, rects, 4);

    CGContextClipToMask (cgc, fgrect, bgmask);
    CGContextFillRect (cgc, fgrect);
    CGImageRelease (bgmask);
    bgmask = 0;
    CGContextRestoreGState (cgc);
  }

  CGContextSetRGBFillColor (cgc, fgr,fgg,fgb,fga);
  CGContextClipToMask (cgc, fgrect, fgmask);
  CGContextFillRect (cgc, fgrect);

  CGImageRelease (fgmask);
  fgmask = 0;		
}
  

/* When this timer goes off, we re-generate the bitmap/pixmap,
   and mark the display as invalid.
*/
- (void)clockTick
{
  if (clockTimer && [clockTimer isValid]) {
    [clockTimer invalidate];
    clockTimer = 0;
  }

  if (config.max_fps <= 0) abort();

    struct timeval now;
    struct timezone tzp;
    gettimeofday (&now, &tzp);
    render_once (&config, now.tv_sec, now.tv_usec);

    [self updateScreen];
 

  // re-schedule the timer according to current fps.
  //
  float delay = 0.9 / config.max_fps;
  clockTimer = [NSTimer scheduledTimerWithTimeInterval:delay
                                                target:self
                                              selector:@selector(clockTick)
                                              userInfo:nil
                                               repeats:NO];
}

- (void) intializeDigits:(CGRect) frame
{
	NSLog(@"initDigits");
	int ow = config.width;
	int oh = config.height;

	config.width  = (int) frame.size.width;   // use the next-larger bitmap
	config.height = (int) frame.size.height;
	NSLog(@"fsw: %f, fsh: %f", frame.size.width, frame.size.height);
	
	NSLog(@"ch: %d, cw: %d", config.height, config.width);
	
	//  config.width = 1280;    // always use the biggest font image
	//  config.height = 1024;

	render_bitmap_size (&config, &config.width, &config.height);
    NSLog(@"ch: %d, cw: %d", config.height, config.width);
	if (config.render_state && (ow == config.width && oh == config.height))
	 return;  // nothing to do

	// When the window is resized, re-create the bitmaps for the largest
	// font that will now fit in the window.
	//
	if (config.bitmap) free (config.bitmap);
	config.bitmap = calloc (1, config.height * (config.width << 3));
	if (! config.bitmap) abort();

	if (pixmap) free (pixmap);
	pixmap = calloc (1, config.height * config.width * 4);
	if (! pixmap) abort();

	if (config.render_state)
	 	render_free (&config);
	render_init (&config);
	NSLog(@"initDigits Done");
	NSLog(@"fsw: %f, fsh: %f", frame.size.width, frame.size.height);
	
	NSLog(@"ch: %d, cw: %d", config.height, config.width);
}

/* When this timer goes off, we re-pick the foreground/background colors,
   and mark the display as invalid.*/
 - (void)colorTick
{
	if (colorTimer && [colorTimer isValid]) {
		[colorTimer invalidate];
		colorTimer = 0;
	}

	float tick = 1.0 / 360.0;   // cycle H by one degree per tick

	fgh += tick;
	while (fgh > 1.0) fgh -= 1.0;
	
	unsigned short r, g, b;
	hsv_to_rgb (fgh * 360, fgs, fgv, &r, &g, &b);
	fgcolor = ((b << 16) + (g << 8) + b);
	
	bgh += tick * 0.91;   // cycle bg slightly slower than fg, for randomosity.
	while (bgh > 1.0) bgh -= 1.0;
	NSLog(@"fg = %f,%f,%f bgh = %f,%f,%f", fgh, fgs, fgv, bgh, bgs, bgv);
	hsv_to_rgb (bgh * 360, bgs, bgv, &r, &g, &b);
    bgcolor = ((b << 16) + (g << 8) + b);

	//NSLog(@"fgcolor = %x, bgcolor = %x", fgcolor, bgcolor);
	//[self setForeground:fg2 background:bg2]; 

	/* re-schedule the timer according to current fps. */
	 
	float delay = 1.0 / config.max_cps;
	colorTimer = [NSTimer scheduledTimerWithTimeInterval:delay
	                                              target:self
	                                            selector:@selector(colorTick)
	                                            userInfo:nil
	                                             repeats:NO];
}

- (BOOL)ignoresMouseEvents {

    return NO;
}

- (void)touchesBegan:touches withEvent:event {
	//CGPoint point = [touches 
	//NSLog(@"mouseDown %f, %f", point.x, point.y);
	config.display_date_p = 1;
}

- (void)touchesEnded:touches withEvent:event {
	//CGPoint point = GSEventGetLocationInWindow(event);
	//NSLog(@"mouseUp %f, %f", point.x, point.y);
	config.display_date_p = 0;
}

- (void)deviceOrientationChanged:(int)screenOrientation {
	NSLog(@"Device orientation changed. From %d, to %d", __screenOrientation, screenOrientation);
	__screenOrientation = screenOrientation;
	/*    if (currentView != CUR_EMULATOR)
	        return;

	    rect = [ UIHardware fullScreenApplicationContentRect ];
	    rect.origin.x = rect.origin.y = 0.0f;

	    [ transitionView removeFromSuperview ];
	    [ transitionView release ];

	    EmulationView *newEmuView = [ self createEmulationView ];

	    transitionView = [ self createTransitionView ];
	    [ self addSubview: transitionView ];
	    [ transitionView transition:6 fromView:emuView toView:newEmuView ];

	    emuView = newEmuView;

	    if (__screenOrientation > 2) {
	        [ navBar removeFromSuperview ];
	    } else {
	        [ self addSubview: navBar ];
	        [ self setNavBar ];
	    }
	}*/
}

@end
